//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;

namespace OpenLS.Chart.UI.Internals
{
    ///<summary>
    /// Internal axis info, don't use outside
    ///</summary>
    public class AxisInfo
    {
        private readonly double _maximum;
        private readonly double _minimum;
        private double _step;

        public AxisInfo(double? axisMinimum, double? axisMaximum, double? dataMinimum, double? dataMaximum)
        {
            var bounds = GetBounds(dataMinimum.GetValueOrDefault(0), dataMaximum.GetValueOrDefault(1));
            _minimum = axisMinimum != null ? axisMinimum.Value : bounds.Low;
            _maximum = axisMaximum != null ? axisMaximum.Value : bounds.High;
            _step = getStep(_minimum, _maximum, 6);
        }

        public double Minimum
        {
            get { return _minimum; }
        }

        public double Maximum
        {
            get { return _maximum; }
        }

        public double Step
        {
            get { return _step; }
        }

        public IEnumerable<NumericValue> Values
        {
            get { return ValueProvider.GetNumericValues(Minimum, Maximum, Step); }
        }

        internal static double getStep(double min, double max, int maxItemCount)
        {
            double delta = (max - min)/maxItemCount;
            double l = Math.Floor(Math.Log10(delta));
            delta = Math.Pow(10, l);
            int c2 = itemCount(min, max, delta*2);
            int c3 = itemCount(min, max, delta*5);
            if (c3 >= maxItemCount)
                delta *= 5;
            else if (c2 >= maxItemCount)
                delta *= 2;
            return delta;
        }


        internal static Bounds GetBounds(double min, double max)
        {
            //min = Math.Min(min, double.Epsilon);
            //max = Math.Max(max, 0);
            Contract.Requires(min <= max);
            if (min == max)
            {
                var result = new Bounds {Low = min - 1, High = max + 1};
                return result;
            }
            else
            {
                double delta = (max - min)/6;
                double l = Math.Floor(Math.Log10(delta));
                delta = Math.Pow(10, l);
                //itemCount(min, max, delta);
                int c2 = itemCount(min, max, delta*2);
                int c3 = itemCount(min, max, delta*5);
                if (c3 >= 4)
                    delta *= 5;
                else if (c2 >= 4)
                    delta *= 2;
                var i = (int) Math.Floor((min - double.Epsilon)/delta);
                if (i * delta >= min)
                    i--;
                var result = new Bounds {Low = i*delta};
                while (true)
                {
                    if (i*delta > max)
                    {
                        result.High = i*delta;
                        if (result.Low > 0 && result.Delta * 3 > result.High || result.Delta == 0)
                            result.Low = 0;
                        return result;
                    }
                    i++;
                }
            }
        }

        private static int itemCount(double min, double max, double delta)
        {
            var i = (int) Math.Floor(min/delta);
            var j = (int) Math.Ceiling(max/delta);
            return j - i;
        }
    }
}